{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Week 2 Day 2\n",
    "\n",
    "Our first Agentic Framework project!!\n",
    "\n",
    "Prepare yourself for something ridiculously easy.\n",
    "\n",
    "We're going to build a simple Agent system for generating cold sales outreach emails:\n",
    "1. Agent workflow\n",
    "2. Use of tools to call functions\n",
    "3. Agent collaboration via Tools and Handoffs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Before we start - some setup:\n",
    "\n",
    "\n",
    "Please visit Resend at: https://resend.com/\n",
    "\n",
    "(Resend is a modern email API service for developers.)\n",
    "\n",
    "Please set up an account - it's free! (at least for the first 3,000 emails per month).\n",
    "\n",
    "Once you've created an account:\n",
    "\n",
    "1. **Get your API Key:**\n",
    "   - Go to API Keys in your dashboard\n",
    "   - Click \"Create API Key\"\n",
    "   - Give it a name (e.g., \"Sales Agent\")\n",
    "   - Copy the key to the clipboard\n",
    "\n",
    "2. **Add the key to your .env file:**\n",
    "   ```\n",
    "   RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxxxxxxxx\n",
    "   ```\n",
    "\n",
    "3. **Set up your domain:**\n",
    "   - Go to Domains in your dashboard\n",
    "   - Click \"Add Domain\"\n",
    "   - Add your domain (e.g., yourdomain.com)\n",
    "   - Follow the DNS verification steps\n",
    "   - Or use the default `onboarding@resend.dev` for testing\n",
    "\n",
    "4. **Update the sender email:**\n",
    "   - Make sure to update the `from` field in the functions to use your verified domain\n",
    "   - Format: `yourname@yourdomain.com` or `onboarding@resend.dev` for testing\n",
    "   - **Important:** Replace `[youremail@yourdomain.com]` and `[recipient@example.com]` in the code with your actual email addresses\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "load_dotenv(override=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Step 1: Agent workflow"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [],
   "source": [
    "instructions1 = \"You are a sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write professional, serious cold emails.\"\n",
    "\n",
    "instructions2 = \"You are a humorous, engaging sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write witty, engaging cold emails that are likely to get a response.\"\n",
    "\n",
    "instructions3 = \"You are a busy sales agent working for ComplAI, \\\n",
    "a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. \\\n",
    "You write concise, to the point cold emails.\""
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [],
   "source": [
    "sales_agent1 = Agent(\n",
    "        name=\"Professional Sales Agent\",\n",
    "        instructions=instructions1,\n",
    "        model=\"gpt-4o-mini\"\n",
    ")\n",
    "\n",
    "sales_agent2 = Agent(\n",
    "        name=\"Engaging Sales Agent\",\n",
    "        instructions=instructions2,\n",
    "        model=\"gpt-4o-mini\"\n",
    ")\n",
    "\n",
    "sales_agent3 = Agent(\n",
    "        name=\"Busy Sales Agent\",\n",
    "        instructions=instructions3,\n",
    "        model=\"gpt-4o-mini\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "result = Runner.run_streamed(sales_agent1, input=\"Write a cold sales email\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "message = \"Write a cold sales email\"\n",
    "\n",
    "with trace(\"Parallel cold emails\"):\n",
    "    results = await asyncio.gather(\n",
    "        Runner.run(sales_agent1, message),\n",
    "        Runner.run(sales_agent2, message),\n",
    "        Runner.run(sales_agent3, message),\n",
    "    )\n",
    "\n",
    "outputs = [result.final_output for result in results]\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [],
   "source": [
    "sales_picker = Agent(\n",
    "    name=\"sales_picker\",\n",
    "    instructions=\"You pick the best cold sales email from the given options. \\\n",
    "Imagine you are a customer and pick the one you are most likely to respond to. \\\n",
    "Do not give an explanation; reply with the selected email only.\",\n",
    "    model=\"gpt-4o-mini\"\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Best sales email:\n",
      "Subject: Is Your SOC2 Compliance Giving You Nightmares? 😱\n",
      "\n",
      "Hey [First Name],\n",
      "\n",
      "Ever tried to sleep while dreaming of endless audit checklists and compliance nightmares? 😴💤\n",
      "\n",
      "Let’s face it: juggling compliance can make you feel like a circus performer walking a tightrope, but luckily, you don’t have to juggle this alone! 🎪\n",
      "\n",
      "At ComplAI, we’ve developed a cozy little SaaS tool that takes the “Ugh!” out of SOC2 compliance. Powered by AI, our solution might just be the best thing since sliced bread—if sliced bread could perform audits without breaking a sweat. 🥖✨\n",
      "\n",
      "With ComplAI, you can:\n",
      "\n",
      "- **Breeze through audits**: Because no one has time for nail-biting moments.\n",
      "- **Automate compliance**: So you can focus on what really matters—like what to binge-watch next. 📺\n",
      "- **Sleep peacefully**: (Pajamas optional, but recommended.)\n",
      "\n",
      "Interested? Let’s have a quick chat and turn that audit frown upside down! 😊\n",
      "\n",
      "Looking forward to hearing from you!\n",
      "\n",
      "Best,  \n",
      "[Your Name]  \n",
      "Sales Wizard at ComplAI  \n",
      "[Your Phone Number]  \n",
      "[Your LinkedIn]  \n",
      "\n",
      "P.S. No audits were harmed in the making of this email! 🦄\n"
     ]
    }
   ],
   "source": [
    "message = \"Write a cold sales email\"\n",
    "\n",
    "with trace(\"Selection from sales people\"):\n",
    "    results = await asyncio.gather(\n",
    "        Runner.run(sales_agent1, message),\n",
    "        Runner.run(sales_agent2, message),\n",
    "        Runner.run(sales_agent3, message),\n",
    "    )\n",
    "    outputs = [result.final_output for result in results]\n",
    "\n",
    "    emails = \"Cold sales emails:\\n\\n\".join(outputs)\n",
    "\n",
    "    best = await Runner.run(sales_picker, emails)\n",
    "\n",
    "    print(f\"Best sales email:\\n{best.final_output}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now go and check out the trace:\n",
    "\n",
    "https://platform.openai.com/traces"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Part 2: use of tools\n",
    "\n",
    "Now we will add a tool to the mix.\n",
    "\n",
    "Remember all that json boilerplate and the `handle_tool_calls()` function with the if logic.."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [],
   "source": [
    "sales_agent1 = Agent(\n",
    "        name=\"Professional Sales Agent\",\n",
    "        instructions=instructions1,\n",
    "        model=\"gpt-4o-mini\",\n",
    ")\n",
    "\n",
    "sales_agent2 = Agent(\n",
    "        name=\"Engaging Sales Agent\",\n",
    "        instructions=instructions2,\n",
    "        model=\"gpt-4o-mini\",\n",
    ")\n",
    "\n",
    "sales_agent3 = Agent(\n",
    "        name=\"Busy Sales Agent\",\n",
    "        instructions=instructions3,\n",
    "        model=\"gpt-4o-mini\",\n",
    ")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Agent(name='Professional Sales Agent', instructions='You are a sales agent working for ComplAI, a company that provides a SaaS tool for ensuring SOC2 compliance and preparing for audits, powered by AI. You write professional, serious cold emails.', handoff_description=None, handoffs=[], model='gpt-4o-mini', model_settings=ModelSettings(temperature=None, top_p=None, frequency_penalty=None, presence_penalty=None, tool_choice=None, parallel_tool_calls=None, truncation=None, max_tokens=None, reasoning=None, metadata=None, store=None, include_usage=None, extra_query=None, extra_body=None, extra_headers=None), tools=[], mcp_servers=[], mcp_config={}, input_guardrails=[], output_guardrails=[], output_type=None, hooks=None, tool_use_behavior='run_llm_again', reset_tool_choice=True)"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "sales_agent1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Steps 2 and 3: Tools and Agent interactions\n",
    "\n",
    "Remember all that boilerplate json?\n",
    "\n",
    "Simply wrap your function with the decorator `@function_tool`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@function_tool\n",
    "def send_email(body: str):\n",
    "    \"\"\" Send out an email with the given body to all sales prospects \"\"\"\n",
    "    resend.api_key = os.environ.get('RESEND_API_KEY')\n",
    "    \n",
    "    params = {\n",
    "        \"from\": \"[youremail@yourdomain.com]\",  # Change to your verified sender domain\n",
    "        \"to\": [\"[recipient@example.com]\"],  # Change to your recipient\n",
    "        \"subject\": \"Sales email\",\n",
    "        \"text\": body\n",
    "    }\n",
    "    \n",
    "    response = resend.Emails.send(params)\n",
    "    return {\"status\": \"success\", \"id\": response.get(\"id\")}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "from dotenv import load_dotenv\n",
    "from agents import Agent, Runner, trace, function_tool\n",
    "from openai.types.responses import ResponseTextDeltaEvent\n",
    "from typing import Dict\n",
    "import resend\n",
    "import os\n",
    "import asyncio\n",
    "\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### This has automatically been converted into a tool, with the boilerplate json created"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "FunctionTool(name='send_email', description='Send out an email with the given body to all sales prospects', params_json_schema={'properties': {'body': {'title': 'Body', 'type': 'string'}}, 'required': ['body'], 'title': 'send_email_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x113172c00>, strict_json_schema=True)"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Let's look at it\n",
    "send_email"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### And you can also convert an Agent into a tool"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "FunctionTool(name='sales_agent1', description='Write a cold sales email', params_json_schema={'properties': {'input': {'title': 'Input', 'type': 'string'}}, 'required': ['input'], 'title': 'sales_agent1_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x113278ea0>, strict_json_schema=True)"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "tool1 = sales_agent1.as_tool(tool_name=\"sales_agent1\", tool_description=\"Write a cold sales email\")\n",
    "tool1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### So now we can gather all the tools together:\n",
    "\n",
    "A tool for each of our 3 email-writing agents\n",
    "\n",
    "And a tool for our function to send emails"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "[FunctionTool(name='sales_agent1', description='Write a cold sales email', params_json_schema={'properties': {'input': {'title': 'Input', 'type': 'string'}}, 'required': ['input'], 'title': 'sales_agent1_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x11285b600>, strict_json_schema=True),\n",
       " FunctionTool(name='sales_agent2', description='Write a cold sales email', params_json_schema={'properties': {'input': {'title': 'Input', 'type': 'string'}}, 'required': ['input'], 'title': 'sales_agent2_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x111142480>, strict_json_schema=True),\n",
       " FunctionTool(name='sales_agent3', description='Write a cold sales email', params_json_schema={'properties': {'input': {'title': 'Input', 'type': 'string'}}, 'required': ['input'], 'title': 'sales_agent3_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x1131c11c0>, strict_json_schema=True),\n",
       " FunctionTool(name='send_email', description='Send out an email with the given body to all sales prospects', params_json_schema={'properties': {'body': {'title': 'Body', 'type': 'string'}}, 'required': ['body'], 'title': 'send_email_args', 'type': 'object', 'additionalProperties': False}, on_invoke_tool=<function function_tool.<locals>._create_function_tool.<locals>._on_invoke_tool at 0x113172c00>, strict_json_schema=True)]"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "description = \"Write a cold sales email\"\n",
    "\n",
    "tool1 = sales_agent1.as_tool(tool_name=\"sales_agent1\", tool_description=description)\n",
    "tool2 = sales_agent2.as_tool(tool_name=\"sales_agent2\", tool_description=description)\n",
    "tool3 = sales_agent3.as_tool(tool_name=\"sales_agent3\", tool_description=description)\n",
    "\n",
    "tools = [tool1, tool2, tool3, send_email]\n",
    "\n",
    "tools"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## And now it's time for our Sales Manager - our planning agent"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "RunResult:\n",
      "- Last agent: Agent(name=\"Sales Manager\", ...)\n",
      "- Final output (str):\n",
      "    The cold sales email has been successfully sent to the CEO. If you need any more assistance or would like to send another email, feel free to ask!\n",
      "- 9 new item(s)\n",
      "- 3 raw response(s)\n",
      "- 0 input guardrail result(s)\n",
      "- 0 output guardrail result(s)\n",
      "(See `RunResult` for more details)\n"
     ]
    }
   ],
   "source": [
    "instructions =\"You are a sales manager working for ComplAI. You use the tools given to you to generate cold sales emails. \\\n",
    "You never generate sales emails yourself; you always use the tools. \\\n",
    "You try all 3 sales_agent tools once before choosing the best one. \\\n",
    "You pick the single best email and use the send_email tool to send the best email (and only the best email) to the user.\"\n",
    "\n",
    "\n",
    "sales_manager = Agent(name=\"Sales Manager\", instructions=instructions, tools=tools, model=\"gpt-4o-mini\")\n",
    "\n",
    "message = \"Send a cold sales email addressed to 'Dear CEO'\"\n",
    "\n",
    "with trace(\"Sales manager\"):\n",
    "    result = await Runner.run(sales_manager, message)\n",
    "\n",
    "print(result)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table style=\"margin: 0; text-align: left; width:100%\">\n",
    "    <tr>\n",
    "        <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
    "            <img src=\"../assets/stop.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
    "        </td>\n",
    "        <td>\n",
    "            <h2 style=\"color:#ff7800;\">Wait - you didn't get an email??</h2>\n",
    "            <span style=\"color:#ff7800;\">With much thanks to student Chris S. for describing his issue and fixes. \n",
    "            If you don't receive an email after running the prior cell, here are some things to check: <br/>\n",
    "            First, check your Spam folder! Several students have missed that the emails arrived in Spam!<br/>Second, print(result) and see if you are receiving errors about SSL. \n",
    "            If you're receiving SSL errors, then please check out theses <a href=\"https://chatgpt.com/share/680620ec-3b30-8012-8c26-ca86693d0e3d\">networking tips</a> and see the note in the next cell. Also look at the trace in OpenAI, and investigate on the SendGrid website, to hunt for clues. Let me know if I can help!\n",
    "            </span>\n",
    "        </td>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### And one more suggestion to send emails from student Oleksandr on Windows 11:\n",
    "\n",
    "If you are getting certificate SSL errors, then:  \n",
    "Run this in a terminal: `uv pip install --upgrade certifi`\n",
    "\n",
    "Then run this code:\n",
    "```python\n",
    "import certifi\n",
    "import os\n",
    "os.environ['SSL_CERT_FILE'] = certifi.where()\n",
    "```\n",
    "\n",
    "Thank you Oleksandr!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Remember to check the trace\n",
    "\n",
    "https://platform.openai.com/traces\n",
    "\n",
    "And then check your email!!\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Handoffs represent a way an agent can delegate to an agent, passing control to it\n",
    "\n",
    "Handoffs and Agents-as-tools are similar:\n",
    "\n",
    "In both cases, an Agent can collaborate with another Agent\n",
    "\n",
    "With tools, control passes back\n",
    "\n",
    "With handoffs, control passes across\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "subject_instructions = \"You can write a subject for a cold sales email. \\\n",
    "You are given a message and you need to write a subject for an email that is likely to get a response.\"\n",
    "\n",
    "html_instructions = \"You can convert a text email body to an HTML email body. \\\n",
    "You are given a text email body which might have some markdown \\\n",
    "and you need to convert it to an HTML email body with simple, clear, compelling layout and design.\"\n",
    "\n",
    "subject_writer = Agent(name=\"Email subject writer\", instructions=subject_instructions, model=\"gpt-4o-mini\")\n",
    "subject_tool = subject_writer.as_tool(tool_name=\"subject_writer\", tool_description=\"Write a subject for a cold sales email\")\n",
    "\n",
    "html_converter = Agent(name=\"HTML email body converter\", instructions=html_instructions, model=\"gpt-4o-mini\")\n",
    "html_tool = html_converter.as_tool(tool_name=\"html_converter\",tool_description=\"Convert a text email body to an HTML email body\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "@function_tool\n",
    "def send_html_email(subject: str, html_body: str) -> Dict[str, str]:\n",
    "    \"\"\" Send out an email with the given subject and HTML body to all sales prospects \"\"\"\n",
    "    resend.api_key = os.environ.get('RESEND_API_KEY')\n",
    "    \n",
    "    params = {\n",
    "        \"from\": \"[youremail@yourdomain.com]\",  # Change to your verified sender domain\n",
    "        \"to\": [\"[recipient@example.com]\"],  # Change to your recipient\n",
    "        \"subject\": subject,\n",
    "        \"html\": html_body\n",
    "    }\n",
    "    \n",
    "    response = resend.Emails.send(params)\n",
    "    return {\"status\": \"success\", \"id\": response.get(\"id\")}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [subject_tool, html_tool, send_html_email]"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "instructions =\"You are an email formatter and sender. You receive the body of an email to be sent. \\\n",
    "You first use the subject_writer tool to write a subject for the email, then use the html_converter tool to convert the body to HTML. \\\n",
    "Finally, you use the send_html_email tool to send the email with the subject and HTML body.\"\n",
    "\n",
    "\n",
    "emailer_agent = Agent(\n",
    "    name=\"Email Manager\",\n",
    "    instructions=instructions,\n",
    "    tools=tools,\n",
    "    model=\"gpt-4o-mini\",\n",
    "    handoff_description=\"Convert an email to HTML and send it\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Now we have 3 tools and 1 handoff"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "tools = [tool1, tool2, tool3]\n",
    "handoffs = [emailer_agent]\n",
    "print(tools)\n",
    "print(handoffs)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "sales_manager_instructions = \"You are a sales manager working for ComplAI. You use the tools given to you to generate cold sales emails. \\\n",
    "You never generate sales emails yourself; you always use the tools. \\\n",
    "You try all 3 sales agent tools at least once before choosing the best one. \\\n",
    "You can use the tools multiple times if you're not satisfied with the results from the first try. \\\n",
    "You select the single best email using your own judgement of which email will be most effective. \\\n",
    "After picking the email, you handoff to the Email Manager agent to format and send the email.\"\n",
    "\n",
    "\n",
    "sales_manager = Agent(\n",
    "    name=\"Sales Manager\",\n",
    "    instructions=sales_manager_instructions,\n",
    "    tools=tools,\n",
    "    handoffs=handoffs,\n",
    "    model=\"gpt-4o-mini\")\n",
    "\n",
    "message = \"Send out a cold sales email addressed to Dear CEO from Alice\"\n",
    "\n",
    "with trace(\"Automated SDR\"):\n",
    "    result = await Runner.run(sales_manager, message)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Remember to check the trace\n",
    "\n",
    "https://platform.openai.com/traces\n",
    "\n",
    "And then check your email!!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table style=\"margin: 0; text-align: left; width:100%\">\n",
    "    <tr>\n",
    "        <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
    "            <img src=\"../assets/exercise.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
    "        </td>\n",
    "        <td>\n",
    "            <h2 style=\"color:#ff7800;\">Exercise</h2>\n",
    "            <span style=\"color:#ff7800;\">Can you identify the Agentic design patterns that were used here?<br/>\n",
    "            What is the 1 line that changed this from being an Agentic \"workflow\" to \"agent\" under Anthropic's definition?<br/>\n",
    "            Try adding in more tools and Agents! You could have tools that handle the mail merge to send to a list.<br/><br/>\n",
    "            HARD CHALLENGE: research how you can have SendGrid call a Callback webhook when a user replies to an email,\n",
    "            Then have the SDR respond to keep the conversation going! This may require some \"vibe coding\" 😂\n",
    "            </span>\n",
    "        </td>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "<table style=\"margin: 0; text-align: left; width:100%\">\n",
    "    <tr>\n",
    "        <td style=\"width: 150px; height: 150px; vertical-align: middle;\">\n",
    "            <img src=\"../assets/business.png\" width=\"150\" height=\"150\" style=\"display: block;\" />\n",
    "        </td>\n",
    "        <td>\n",
    "            <h2 style=\"color:#00bfff;\">Commercial implications</h2>\n",
    "            <span style=\"color:#00bfff;\">This is immediately applicable to Sales Automation; but more generally this could be applied to  end-to-end automation of any business process through conversations and tools. Think of ways you could apply an Agent solution\n",
    "            like this in your day job.\n",
    "            </span>\n",
    "        </td>\n",
    "    </tr>\n",
    "</table>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extra note:\n",
    "\n",
    "Google has announced their Agent Development Kit (ADK) which is in early preview. It's still under development, so it's too early for us to cover it here. But it's interesting to note that it looks quite similar to OpenAI Agents SDK. To give you a preview, here's a peak at sample code from ADK:\n",
    "\n",
    "```\n",
    "root_agent = Agent(\n",
    "    name=\"weather_time_agent\",\n",
    "    model=\"gemini-2.0-flash\",\n",
    "    description=\"Agent to answer questions about the time and weather in a city.\",\n",
    "    instruction=\"You are a helpful agent who can answer user questions about the time and weather in a city.\",\n",
    "    tools=[get_weather, get_current_time]\n",
    ")\n",
    "```\n",
    "\n",
    "Well, that looks familiar!"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": ".venv",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.7"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
